/**
 * 
 */
package org.swing.utility.common.net;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * @author lqnhu
 *
 */
public final class HostUtils {
	private HostUtils() {
		// Utility Class
	}

	/**
	 * Returns a {@link Map} of {@link InetAddress} per {@link NetworkInterface}
	 * .
	 */
	public static Map<String, Set<InetAddress>> getNetworkInterfaceAddresses() {
		// JVM returns interfaces in a non-predictable order, so to make this
		// more predictable
		// let's have them sort by interface name (by using a TreeMap).
		Map<String, Set<InetAddress>> interfaceAddressMap = new TreeMap<String, Set<InetAddress>>();
		try {
			Enumeration<NetworkInterface> ifaces = NetworkInterface
					.getNetworkInterfaces();
			while (ifaces.hasMoreElements()) {
				NetworkInterface iface = ifaces.nextElement();
				// We only care about usable non-loopback interfaces.
				if (iface.isUp() && !iface.isLoopback()
						&& !iface.isPointToPoint()) {
					String name = iface.getName();
					Enumeration<InetAddress> ifaceAdresses = iface
							.getInetAddresses();
					while (ifaceAdresses.hasMoreElements()) {
						InetAddress ia = ifaceAdresses.nextElement();
						// We want to filter out mac addresses
						if (!ia.isLoopbackAddress()
								&& !ia.getHostAddress().contains(":")) {
							Set<InetAddress> addresses = interfaceAddressMap
									.get(name);
							if (addresses == null) {
								addresses = new LinkedHashSet<InetAddress>();
							}
							addresses.add(ia);
							interfaceAddressMap.put(name, addresses);
						}
					}
				}
			}
		} catch (SocketException ex) {
			// noop
		}
		return interfaceAddressMap;
	}

	/**
	 * Returns a {@link Set} of {@link InetAddress} that are non-loopback or
	 * mac.
	 */
	public static Set<InetAddress> getAddresses() {
		Set<InetAddress> allAddresses = new LinkedHashSet<InetAddress>();
		Map<String, Set<InetAddress>> interfaceAddressMap = getNetworkInterfaceAddresses();
		for (Map.Entry<String, Set<InetAddress>> entry : interfaceAddressMap
				.entrySet()) {
			Set<InetAddress> addresses = entry.getValue();
			if (!addresses.isEmpty()) {
				for (InetAddress address : addresses) {
					allAddresses.add(address);
				}
			}
		}
		return allAddresses;
	}

	/**
	 * Chooses one of the available {@link InetAddress} based on the specified
	 * preference.
	 */
	private static InetAddress chooseAddress() throws UnknownHostException {
		Set<InetAddress> addresses = getAddresses();
		if (addresses.contains(InetAddress.getLocalHost())) {
			// Then if local host address is not bound to a loop-back interface,
			// use it.
			return InetAddress.getLocalHost();
		} else if (addresses != null && !addresses.isEmpty()) {
			// else return the first available addrress
			return addresses.toArray(new InetAddress[addresses.size()])[0];
		} else {
			// else we are forcedt to use the localhost address.
			return InetAddress.getLocalHost();
		}
	}

	/**
	 * Returns the local hostname. It loops through the network interfaces and
	 * returns the first non loopback hostname
	 */
	public static String getLocalHostName() throws UnknownHostException {
		return chooseAddress().getHostName();
	}

	/**
	 * Returns the local IP. It loops through the network interfaces and returns
	 * the first non loopback address
	 */
	public static String getLocalIp() throws UnknownHostException {
		return chooseAddress().getHostAddress();
	}
}	
